package com.enjoy.james.servlet;

import com.enjoy.james.annotation.*;
import com.enjoy.james.controller.JamesController;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Title: springmvc
 * About:
 * Other:
 * Created: tyy on 11:08 2019/2/22
 * Editored:
 */
public class DispatcherServlet extends HttpServlet {
    List<String> classNames = new ArrayList<>();
    Map<String,Object> beans = new HashMap<String,Object>();
    Map<String,Object> handlerMap = new HashMap<String,Object>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        //扫包
        doScanPackage("com.enjoy");
        //实例化
        doInstance();
        //自动装配di
        doAutowired();
        //地址映射
        urlMapping();
    }

    private void urlMapping() {
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            Object instance = entry.getValue();
            Class<?> clazz = instance.getClass();
            //mapping
            if (clazz.isAnnotationPresent(EnjoyController.class)) {
                //控制类
                EnjoyRequestMapper map1 = clazz.getAnnotation(EnjoyRequestMapper.class);
                String classPath = map1.value();
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (method.isAnnotationPresent(EnjoyRequestMapper.class)) {
                        EnjoyRequestMapper map2 = method.getAnnotation(EnjoyRequestMapper.class);
                        String methodPath = map2.value();
                        handlerMap.put(classPath +methodPath, method);

                    }
                }
            }

        }
    }

    private void doAutowired() {
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            Object instance = entry.getValue();
            Class<?> clazz = instance.getClass();
            if (clazz.isAnnotationPresent(EnjoyController.class)) {
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    if (field.isAnnotationPresent(EnjoyAutowired.class)) {
                        EnjoyAutowired enjoyAutowired = field.getAnnotation(EnjoyAutowired.class);
                        String key = enjoyAutowired.value();
                        Object obj = beans.get(key);
                        field.setAccessible(true);

                        try {
                            field.set(instance, obj);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }else {
                        continue;
                    }
                }
            }

        }
    }

    private void doInstance() {
        for (String className : classNames) {
            String cn = className.replace(".class", "");
            try {
                Class<?> clazz = Class.forName(cn);
                if (clazz.isAnnotationPresent(EnjoyController.class)) {//class对象是否有该注解
                    // 控制类 实例放在ioc map
                    Object instance1 = clazz.newInstance();
                    EnjoyRequestMapper map1 = clazz.getAnnotation(EnjoyRequestMapper.class);
                    String key1 = map1.value();
                    beans.put(key1, instance1); //ioc容器
                }else if (clazz.isAnnotationPresent(EnjoySerice.class)){
                    // 服务类 实例放在ioc map
                    Object instance2 = clazz.newInstance();
                    EnjoySerice map2 = clazz.getAnnotation(EnjoySerice.class);
                    String key2 = map2.value();
                    beans.put(key2, instance2);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }


        }
    }

    private void doScanPackage(String basePackage) {// com.enjoy
        //e://..../com/enjoy...
        URL url = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\\.","/" ));
        String fileStr = url.getFile();
        File file = new File(fileStr);//本地包目录
        String[] filesStr = file.list();//目录下文件（包括目录）
        for (String path : filesStr) {
            File filePath = new File(fileStr+path);//文件（包括目录）
            if (filePath.isDirectory()) {
                doScanPackage(basePackage +"."+ path);
            }else {
                classNames.add(basePackage +"."+ path);// 或者path==filePath.getName()
            }
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取到请求路径 -- method
        // uri = /enjoymvc/james/query
        String uri = req.getRequestURI();
        String context = req.getContextPath();
        String path = uri.replace(context,"" );

        Method method = (Method) handlerMap.get(path);
        JamesController instance = (JamesController)beans.get("/" +path.split("/")[1]);
        Object args[] = hand(req, resp, method);
        try{
            method.invoke(instance, args);
        }catch (Exception e){

        }
    }

    private Object[] hand(HttpServletRequest req, HttpServletResponse resp, Method method) {
        //拿到当前执行的方法有哪些参数
        Class<?>[] paramclazzs = method.getParameterTypes();
        Object[] args = new Object[paramclazzs.length];

        int args_i = 0;
        int index = 0;
        for (Class<?> paramclazz : paramclazzs) {
            //判定此 Class (ServletRequest)类对象所表示的类或接口与指定的 Class 参数(paramclazz)所表示的类或接口是否相同，或是否是其超类或超接口。
            if (ServletRequest.class.isAssignableFrom(paramclazz)) {
                args[args_i++] = req;
            }
            if (ServletRequest.class.isAssignableFrom(paramclazz)) {
                args[args_i++] = resp;
            }
            Annotation[] paramAns = method.getParameterAnnotations()[index];
            if (paramAns.length > 0) {
                for (Annotation paramAn : paramAns) {
                    if (EnjoyRequestParam.class.isAssignableFrom(paramAn.getClass())) {
                        EnjoyRequestParam rp = (EnjoyRequestParam) paramAn;
                        // 找到注解中的name和age
                        args[args_i++] = req.getParameter(rp.value());
                    }
                }
            }
            index++;

        }
        return args;
    }
}
